<!doctype html>
<html>
<head>
<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>

<link href='https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext' rel='stylesheet' type='text/css' /><style type='text/css'>html {overflow-x: initial !important;}:root { --bg-color:#ffffff; --text-color:#333333; --select-text-bg-color:#B5D6FC; --select-text-font-color:auto; --monospace:"Lucida Console",Consolas,"Courier",monospace; --title-bar-height:20px; }
.mac-os-11 { --title-bar-height:28px; }
html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
body { margin: 0px; padding: 0px; height: auto; inset: 0px; font-size: 1rem; line-height: 1.42857; overflow-x: hidden; background: inherit; tab-size: 4; }
iframe { margin: auto; }
a.url { word-break: break-all; }
a:active, a:hover { outline: 0px; }
.in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color); }
#write { margin: 0px auto; height: auto; width: inherit; word-break: normal; overflow-wrap: break-word; position: relative; white-space: normal; overflow-x: visible; padding-top: 36px; }
#write.first-line-indent p { text-indent: 2em; }
#write.first-line-indent li p, #write.first-line-indent p * { text-indent: 0px; }
#write.first-line-indent li { margin-left: 2em; }
.for-image #write { padding-left: 8px; padding-right: 8px; }
body.typora-export { padding-left: 30px; padding-right: 30px; }
.typora-export .footnote-line, .typora-export li, .typora-export p { white-space: pre-wrap; }
.typora-export .task-list-item input { pointer-events: none; }
@media screen and (max-width: 500px) {
  body.typora-export { padding-left: 0px; padding-right: 0px; }
  #write { padding-left: 20px; padding-right: 20px; }
  .CodeMirror-sizer { margin-left: 0px !important; }
  .CodeMirror-gutters { display: none !important; }
}
#write li > figure:last-child { margin-bottom: 0.5rem; }
#write ol, #write ul { position: relative; }
img { max-width: 100%; vertical-align: middle; image-orientation: from-image; }
button, input, select, textarea { color: inherit; font: inherit; }
input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; }
*, ::after, ::before { box-sizing: border-box; }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit; }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative; }
p { line-height: inherit; }
h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 4; }
p { orphans: 4; }
h1 { font-size: 2rem; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.6rem; }
h4 { font-size: 1.4rem; }
h5 { font-size: 1.2rem; }
h6 { font-size: 1rem; }
.md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem; }
.hidden { display: none; }
.md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic; }
a { cursor: pointer; }
sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.7); color: rgb(85, 85, 85); border-radius: 4px; cursor: pointer; }
sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit; }
#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; }
figure { overflow-x: auto; margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px; }
figure > table { margin: 0px; }
tr { break-inside: avoid; break-after: auto; }
thead { display: table-header-group; }
table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; }
table.md-table td { min-width: 32px; }
.CodeMirror-gutters { border-right: 0px; background-color: inherit; }
.CodeMirror-linenumber { user-select: none; }
.CodeMirror { text-align: left; }
.CodeMirror-placeholder { opacity: 0.3; }
.CodeMirror pre { padding: 0px 4px; }
.CodeMirror-lines { padding: 0px; }
div.hr:focus { cursor: none; }
#write pre { white-space: pre-wrap; }
#write.fences-no-line-wrapping pre { white-space: pre; }
#write pre.ty-contain-cm { white-space: normal; }
.CodeMirror-gutters { margin-right: 4px; }
.md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background: inherit; position: relative !important; }
.md-fences-adv-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; }
#write .md-fences.mock-cm { white-space: pre-wrap; }
.md-fences.md-fences-with-lineno { padding-left: 0px; }
#write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto; }
.md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px; }
.CodeMirror-line, twitterwidget { break-inside: avoid; }
.footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em; }
.footnotes + .footnotes { margin-top: 0px; }
.md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; background: 0px 0px; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; -webkit-tap-highlight-color: transparent; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr; }
li div { padding-top: 0px; }
blockquote { margin: 1rem 0px; }
li .mathjax-block, li p { margin: 0.5rem 0px; }
li blockquote { margin: 1rem 0px; }
li { margin: 0px; position: relative; }
blockquote > :last-child { margin-bottom: 0px; }
blockquote > :first-child, li > :first-child { margin-top: 0px; }
.footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal; }
#write .footnote-line { white-space: pre-wrap; }
@media print {
  body, html { border: 1px solid transparent; height: 99%; break-after: avoid; break-before: avoid; font-variant-ligatures: no-common-ligatures; }
  #write { margin-top: 0px; padding-top: 0px; border-color: transparent !important; }
  .typora-export * { -webkit-print-color-adjust: exact; }
  .typora-export #write { break-after: avoid; }
  .typora-export #write::after { height: 0px; }
  .is-mac table { break-inside: avoid; }
  .typora-export-show-outline .typora-export-sidebar { display: none; }
}
.footnote-line { margin-top: 0.714em; font-size: 0.7em; }
a img, img a { cursor: pointer; }
pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background: rgb(204, 204, 204); display: block; overflow-x: hidden; }
p > .md-image:only-child:not(.md-img-error) img, p > img:only-child { display: block; margin: auto; }
#write.first-line-indent p > .md-image:only-child:not(.md-img-error) img { left: -2em; position: relative; }
p > .md-image:only-child { display: inline-block; width: 100%; }
#write .MathJax_Display { margin: 0.8em 0px 0px; }
.md-math-block { width: 100%; }
.md-math-block:not(:empty)::after { display: none; }
.MathJax_ref { fill: currentcolor; }
[contenteditable="true"]:active, [contenteditable="true"]:focus, [contenteditable="false"]:active, [contenteditable="false"]:focus { outline: 0px; box-shadow: none; }
.md-task-list-item { position: relative; list-style-type: none; }
.task-list-item.md-task-list-item { padding-left: 0px; }
.md-task-list-item > input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px); border: none; }
.math { font-size: 1rem; }
.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-radius: 10px; }
.md-toc-content { position: relative; margin-left: 0px; }
.md-toc-content::after, .md-toc::after { display: none; }
.md-toc-item { display: block; color: rgb(65, 131, 196); }
.md-toc-item a { text-decoration: none; }
.md-toc-inner:hover { text-decoration: underline; }
.md-toc-inner { display: inline-block; cursor: pointer; }
.md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700; }
.md-toc-h2 .md-toc-inner { margin-left: 2em; }
.md-toc-h3 .md-toc-inner { margin-left: 4em; }
.md-toc-h4 .md-toc-inner { margin-left: 6em; }
.md-toc-h5 .md-toc-inner { margin-left: 8em; }
.md-toc-h6 .md-toc-inner { margin-left: 10em; }
@media screen and (max-width: 48em) {
  .md-toc-h3 .md-toc-inner { margin-left: 3.5em; }
  .md-toc-h4 .md-toc-inner { margin-left: 5em; }
  .md-toc-h5 .md-toc-inner { margin-left: 6.5em; }
  .md-toc-h6 .md-toc-inner { margin-left: 8em; }
}
a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; }
.footnote-line a:not(.reversefootnote) { color: inherit; }
.md-attr { display: none; }
.md-fn-count::after { content: "."; }
code, pre, samp, tt { font-family: var(--monospace); }
kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); border-radius: 3px; box-shadow: rgba(12, 13, 14, 0.2) 0px 1px 0px, rgb(255, 255, 255) 0px 0px 0px 2px inset; white-space: nowrap; vertical-align: middle; }
.md-comment { color: rgb(162, 127, 3); opacity: 0.6; font-family: var(--monospace); }
code { text-align: left; vertical-align: initial; }
a.md-print-anchor { white-space: pre !important; border-width: initial !important; border-style: none !important; border-color: initial !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; background: 0px 0px !important; text-decoration: initial !important; text-shadow: initial !important; }
.os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif; }
.md-diagram-panel > svg { max-width: 100%; }
[lang="flow"] svg, [lang="mermaid"] svg { max-width: 100%; height: auto; }
[lang="mermaid"] .node text { font-size: 1rem; }
table tr th { border-bottom: 0px; }
video { max-width: 100%; display: block; margin: 0px auto; }
iframe { max-width: 100%; width: 100%; border: none; }
.highlight td, .highlight tr { border: 0px; }
mark { background: rgb(255, 255, 0); color: rgb(0, 0, 0); }
.md-html-inline .md-plain, .md-html-inline strong, mark .md-inline-math, mark strong { color: inherit; }
.md-expand mark .md-meta { opacity: 0.3 !important; }
mark .md-meta { color: rgb(0, 0, 0); }
@media print {
  .typora-export h1, .typora-export h2, .typora-export h3, .typora-export h4, .typora-export h5, .typora-export h6 { break-inside: avoid; }
}
.md-diagram-panel .messageText { stroke: none !important; }
.md-diagram-panel .start-state { fill: var(--node-fill); }
.md-diagram-panel .edgeLabel rect { opacity: 1 !important; }
.md-fences.md-fences-math { font-size: 1em; }
.md-fences-advanced:not(.md-focus) { padding: 0px; white-space: nowrap; border: 0px; }
.md-fences-advanced:not(.md-focus) { background: inherit; }
.typora-export-show-outline .typora-export-content { max-width: 1440px; margin: auto; display: flex; flex-direction: row; }
.typora-export-sidebar { width: 300px; font-size: 0.8rem; margin-top: 80px; margin-right: 18px; }
.typora-export-show-outline #write { --webkit-flex:2; flex: 2 1 0%; }
.typora-export-sidebar .outline-content { position: fixed; top: 0px; max-height: 100%; overflow: hidden auto; padding-bottom: 30px; padding-top: 60px; width: 300px; }
@media screen and (max-width: 1024px) {
  .typora-export-sidebar, .typora-export-sidebar .outline-content { width: 240px; }
}
@media screen and (max-width: 800px) {
  .typora-export-sidebar { display: none; }
}
.outline-content li, .outline-content ul { margin-left: 0px; margin-right: 0px; padding-left: 0px; padding-right: 0px; list-style: none; }
.outline-content ul { margin-top: 0px; margin-bottom: 0px; }
.outline-content strong { font-weight: 400; }
.outline-expander { width: 1rem; height: 1.42857rem; position: relative; display: table-cell; vertical-align: middle; cursor: pointer; padding-left: 4px; }
.outline-expander::before { content: ""; position: relative; font-family: Ionicons; display: inline-block; font-size: 8px; vertical-align: middle; }
.outline-item { padding-top: 3px; padding-bottom: 3px; cursor: pointer; }
.outline-expander:hover::before { content: ""; }
.outline-h1 > .outline-item { padding-left: 0px; }
.outline-h2 > .outline-item { padding-left: 1em; }
.outline-h3 > .outline-item { padding-left: 2em; }
.outline-h4 > .outline-item { padding-left: 3em; }
.outline-h5 > .outline-item { padding-left: 4em; }
.outline-h6 > .outline-item { padding-left: 5em; }
.outline-label { cursor: pointer; display: table-cell; vertical-align: middle; text-decoration: none; color: inherit; }
.outline-label:hover { text-decoration: underline; }
.outline-item:hover { border-color: rgb(245, 245, 245); background-color: var(--item-hover-bg-color); }
.outline-item:hover { margin-left: -28px; margin-right: -28px; border-left: 28px solid transparent; border-right: 28px solid transparent; }
.outline-item-single .outline-expander::before, .outline-item-single .outline-expander:hover::before { display: none; }
.outline-item-open > .outline-item > .outline-expander::before { content: ""; }
.outline-children { display: none; }
.info-panel-tab-wrapper { display: none; }
.outline-item-open > .outline-children { display: block; }
.typora-export .outline-item { padding-top: 1px; padding-bottom: 1px; }
.typora-export .outline-item:hover { margin-right: -8px; border-right: 8px solid transparent; }
.typora-export .outline-expander::before { content: "+"; font-family: inherit; top: -1px; }
.typora-export .outline-expander:hover::before, .typora-export .outline-item-open > .outline-item > .outline-expander::before { content: "−"; }
.typora-export-collapse-outline .outline-children { display: none; }
.typora-export-collapse-outline .outline-item-open > .outline-children, .typora-export-no-collapse-outline .outline-children { display: block; }
.typora-export-no-collapse-outline .outline-expander::before { content: "" !important; }
.typora-export-show-outline .outline-item-active > .outline-item .outline-label { font-weight: 700; }
.md-inline-math-container mjx-container { zoom: 0.95; }


.CodeMirror { height: auto; }
.CodeMirror.cm-s-inner { background: inherit; }
.CodeMirror-scroll { overflow: auto hidden; z-index: 3; }
.CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { background-color: rgb(255, 255, 255); }
.CodeMirror-gutters { border-right: 1px solid rgb(221, 221, 221); background: inherit; white-space: nowrap; }
.CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153); }
.cm-s-inner .cm-keyword { color: rgb(119, 0, 136); }
.cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgb(34, 17, 153); }
.cm-s-inner .cm-number { color: rgb(17, 102, 68); }
.cm-s-inner .cm-def { color: rgb(0, 0, 255); }
.cm-s-inner .cm-variable { color: rgb(0, 0, 0); }
.cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170); }
.cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85); }
.cm-s-inner .cm-string { color: rgb(170, 17, 17); }
.cm-s-inner .cm-property { color: rgb(0, 0, 0); }
.cm-s-inner .cm-operator { color: rgb(152, 26, 26); }
.cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgb(170, 85, 0); }
.cm-s-inner .cm-string-2 { color: rgb(255, 85, 0); }
.cm-s-inner .cm-meta { color: rgb(85, 85, 85); }
.cm-s-inner .cm-qualifier { color: rgb(85, 85, 85); }
.cm-s-inner .cm-builtin { color: rgb(51, 0, 170); }
.cm-s-inner .cm-bracket { color: rgb(153, 153, 119); }
.cm-s-inner .cm-tag { color: rgb(17, 119, 0); }
.cm-s-inner .cm-attribute { color: rgb(0, 0, 204); }
.cm-s-inner .cm-header, .cm-s-inner.cm-header { color: rgb(0, 0, 255); }
.cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgb(0, 153, 0); }
.cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgb(153, 153, 153); }
.cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgb(0, 0, 204); }
.cm-negative { color: rgb(221, 68, 68); }
.cm-positive { color: rgb(34, 153, 34); }
.cm-header, .cm-strong { font-weight: 700; }
.cm-del { text-decoration: line-through; }
.cm-em { font-style: italic; }
.cm-link { text-decoration: underline; }
.cm-error { color: red; }
.cm-invalidchar { color: red; }
.cm-constant { color: rgb(38, 139, 210); }
.cm-defined { color: rgb(181, 137, 0); }
div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0); }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34); }
.cm-s-inner .CodeMirror-activeline-background { background: inherit; }
.CodeMirror { position: relative; overflow: hidden; }
.CodeMirror-scroll { height: 100%; outline: 0px; position: relative; box-sizing: content-box; background: inherit; }
.CodeMirror-sizer { position: relative; }
.CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none; outline: 0px; }
.CodeMirror-vscrollbar { right: 0px; top: 0px; overflow: hidden; }
.CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow: auto hidden; }
.CodeMirror-scrollbar-filler { right: 0px; bottom: 0px; }
.CodeMirror-gutter-filler { left: 0px; bottom: 0px; }
.CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 10px; z-index: 3; overflow-y: hidden; }
.CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; }
.CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: 0px 0px !important; border: none !important; }
.CodeMirror-gutter-background { position: absolute; top: 0px; bottom: 0px; z-index: 4; }
.CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; }
.CodeMirror-lines { cursor: text; }
.CodeMirror pre { border-radius: 0px; border-width: 0px; background: 0px 0px; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; overflow-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible; }
.CodeMirror-wrap pre { overflow-wrap: break-word; white-space: pre-wrap; word-break: normal; }
.CodeMirror-code pre { border-right: 30px solid transparent; width: fit-content; }
.CodeMirror-wrap .CodeMirror-code pre { border-right: none; width: auto; }
.CodeMirror-linebackground { position: absolute; inset: 0px; z-index: 0; }
.CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; }
.CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; }
.CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden; }
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right: none; width: 0px; }
.CodeMirror div.CodeMirror-cursor { visibility: hidden; }
.CodeMirror-focused div.CodeMirror-cursor { visibility: inherit; }
.cm-searching { background: rgba(255, 255, 0, 0.4); }
span.cm-underlined { text-decoration: underline; }
span.cm-strikethrough { text-decoration: line-through; }
.cm-tw-syntaxerror { color: rgb(255, 255, 255); background-color: rgb(153, 0, 0); }
.cm-tw-deleted { text-decoration: line-through; }
.cm-tw-header5 { font-weight: 700; }
.cm-tw-listitem:first-child { padding-left: 10px; }
.cm-tw-box { border-style: solid; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-color: inherit; border-top-width: 0px !important; }
.cm-tw-underline { text-decoration: underline; }
@media print {
  .CodeMirror div.CodeMirror-cursor { visibility: hidden; }
}


:root {
    --side-bar-bg-color: #fafafa;
    --control-text-color: #777;
}

@include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext);

/* open-sans-regular - latin-ext_latin */
  /* open-sans-italic - latin-ext_latin */
    /* open-sans-700 - latin-ext_latin */
    /* open-sans-700italic - latin-ext_latin */
  html {
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
}

body {
    font-family: "Open Sans","Clear Sans", "Helvetica Neue", Helvetica, Arial, 'Segoe UI Emoji', sans-serif;
    color: rgb(51, 51, 51);
    line-height: 1.6;
}

#write {
    max-width: 860px;
  	margin: 0 auto;
  	padding: 30px;
    padding-bottom: 100px;
}

@media only screen and (min-width: 1400px) {
	#write {
		max-width: 1024px;
	}
}

@media only screen and (min-width: 1800px) {
	#write {
		max-width: 1200px;
	}
}

#write > ul:first-child,
#write > ol:first-child{
    margin-top: 30px;
}

a {
    color: #4183C4;
}
h1,
h2,
h3,
h4,
h5,
h6 {
    position: relative;
    margin-top: 1rem;
    margin-bottom: 1rem;
    font-weight: bold;
    line-height: 1.4;
    cursor: text;
}
h1:hover a.anchor,
h2:hover a.anchor,
h3:hover a.anchor,
h4:hover a.anchor,
h5:hover a.anchor,
h6:hover a.anchor {
    text-decoration: none;
}
h1 tt,
h1 code {
    font-size: inherit;
}
h2 tt,
h2 code {
    font-size: inherit;
}
h3 tt,
h3 code {
    font-size: inherit;
}
h4 tt,
h4 code {
    font-size: inherit;
}
h5 tt,
h5 code {
    font-size: inherit;
}
h6 tt,
h6 code {
    font-size: inherit;
}
h1 {
    font-size: 2.25em;
    line-height: 1.2;
    border-bottom: 1px solid #eee;
}
h2 {
    font-size: 1.75em;
    line-height: 1.225;
    border-bottom: 1px solid #eee;
}

/*@media print {
    .typora-export h1,
    .typora-export h2 {
        border-bottom: none;
        padding-bottom: initial;
    }

    .typora-export h1::after,
    .typora-export h2::after {
        content: "";
        display: block;
        height: 100px;
        margin-top: -96px;
        border-top: 1px solid #eee;
    }
}*/

h3 {
    font-size: 1.5em;
    line-height: 1.43;
}
h4 {
    font-size: 1.25em;
}
h5 {
    font-size: 1em;
}
h6 {
   font-size: 1em;
    color: #777;
}
p,
blockquote,
ul,
ol,
dl,
table{
    margin: 0.8em 0;
}
li>ol,
li>ul {
    margin: 0 0;
}
hr {
    height: 2px;
    padding: 0;
    margin: 16px 0;
    background-color: #e7e7e7;
    border: 0 none;
    overflow: hidden;
    box-sizing: content-box;
}

li p.first {
    display: inline-block;
}
ul,
ol {
    padding-left: 30px;
}
ul:first-child,
ol:first-child {
    margin-top: 0;
}
ul:last-child,
ol:last-child {
    margin-bottom: 0;
}
blockquote {
    border-left: 4px solid #dfe2e5;
    padding: 0 15px;
    color: #777777;
}
blockquote blockquote {
    padding-right: 0;
}
table {
    padding: 0;
    word-break: initial;
}
table tr {
    border: 1px solid #dfe2e5;
    margin: 0;
    padding: 0;
}
table tr:nth-child(2n),
thead {
    background-color: #f8f8f8;
}
table th {
    font-weight: bold;
    border: 1px solid #dfe2e5;
    border-bottom: 0;
    margin: 0;
    padding: 6px 13px;
}
table td {
    border: 1px solid #dfe2e5;
    margin: 0;
    padding: 6px 13px;
}
table th:first-child,
table td:first-child {
    margin-top: 0;
}
table th:last-child,
table td:last-child {
    margin-bottom: 0;
}

.CodeMirror-lines {
    padding-left: 4px;
}

.code-tooltip {
    box-shadow: 0 1px 1px 0 rgba(0,28,36,.3);
    border-top: 1px solid #eef2f2;
}

.md-fences,
code,
tt {
    border: 1px solid #e7eaed;
    background-color: #f8f8f8;
    border-radius: 3px;
    padding: 0;
    padding: 2px 4px 0px 4px;
    font-size: 0.9em;
}

code {
    background-color: #f3f4f4;
    padding: 0 2px 0 2px;
}

.md-fences {
    margin-bottom: 15px;
    margin-top: 15px;
    padding-top: 8px;
    padding-bottom: 6px;
}


.md-task-list-item > input {
  margin-left: -1.3em;
}

@media print {
    html {
        font-size: 13px;
    }
    table,
    pre {
        page-break-inside: avoid;
    }
    pre {
        word-wrap: break-word;
    }
}

.md-fences {
	background-color: #f8f8f8;
}
#write pre.md-meta-block {
	padding: 1rem;
    font-size: 85%;
    line-height: 1.45;
    background-color: #f7f7f7;
    border: 0;
    border-radius: 3px;
    color: #777777;
    margin-top: 0 !important;
}

.mathjax-block>.code-tooltip {
	bottom: .375rem;
}

.md-mathjax-midline {
    background: #fafafa;
}

#write>h3.md-focus:before{
	left: -1.5625rem;
	top: .375rem;
}
#write>h4.md-focus:before{
	left: -1.5625rem;
	top: .285714286rem;
}
#write>h5.md-focus:before{
	left: -1.5625rem;
	top: .285714286rem;
}
#write>h6.md-focus:before{
	left: -1.5625rem;
	top: .285714286rem;
}
.md-image>.md-meta {
    /*border: 1px solid #ddd;*/
    border-radius: 3px;
    padding: 2px 0px 0px 4px;
    font-size: 0.9em;
    color: inherit;
}

.md-tag {
    color: #a7a7a7;
    opacity: 1;
}

.md-toc { 
    margin-top:20px;
    padding-bottom:20px;
}

.sidebar-tabs {
    border-bottom: none;
}

#typora-quick-open {
    border: 1px solid #ddd;
    background-color: #f8f8f8;
}

#typora-quick-open-item {
    background-color: #FAFAFA;
    border-color: #FEFEFE #e5e5e5 #e5e5e5 #eee;
    border-style: solid;
    border-width: 1px;
}

/** focus mode */
.on-focus-mode blockquote {
    border-left-color: rgba(85, 85, 85, 0.12);
}

header, .context-menu, .megamenu-content, footer{
    font-family: "Segoe UI", "Arial", sans-serif;
}

.file-node-content:hover .file-node-icon,
.file-node-content:hover .file-node-open-state{
    visibility: visible;
}

.mac-seamless-mode #typora-sidebar {
    background-color: #fafafa;
    background-color: var(--side-bar-bg-color);
}

.md-lang {
    color: #b4654d;
}

/*.html-for-mac {
    --item-hover-bg-color: #E6F0FE;
}*/

#md-notification .btn {
    border: 0;
}

.dropdown-menu .divider {
    border-color: #e5e5e5;
    opacity: 0.4;
}

.ty-preferences .window-content {
    background-color: #fafafa;
}

.ty-preferences .nav-group-item.active {
    color: white;
    background: #999;
}

.menu-item-container a.menu-style-btn {
    background-color: #f5f8fa;
    background-image: linear-gradient( 180deg , hsla(0, 0%, 100%, 0.8), hsla(0, 0%, 100%, 0)); 
}



</style><title>操作系统</title>
</head>
<body class='typora-export os-windows'><div class='typora-export-content'>
<div id='write'  class=''><h1 id='以面试题目的角度复习操作系统'><span>以面试题目的角度复习操作系统</span></h1><p><span>某网站网“操作系统”的课程笔记，每一节以一个常见操作系统相关的面试题为切入点，详细剖析背后的知识点。</span></p><p><strong><span>结构：</span></strong></p><ol start='' ><li><span>Linux指令</span></li><li><span>组成原理</span></li><li><span>基础知识</span></li><li><span>线程和进程</span></li><li><span>内存管理</span></li><li><span>文件系统</span></li><li><span>虚拟化技术</span></li><li><span>网络和通信</span></li></ol><p>&nbsp;</p><p>&nbsp;</p><p><span>学习方法总结：1  知识在于一朝一夕的积累，多维度的想法和方法。2 探索式学习，学习过程像攻破一个谜题，或者分析一个案件，并不是从结论开始，然后一层层学习理论；而是通过找到一个目标，一层层挖掘需要的知识、理论，一点点去思考解决方案，最终达到提升解决问题能力的目的。3 如果你对每天发生的事情背后的技术原理保持好奇心和兴趣，并且愿意花时间去探索和学习，这就是技术潜力强的表现；相反，如果你只对马上能为自己创造价值的事情感兴趣，不愿意通过探索和思考的方式，去理解普遍存在的世界，长此以往就会导致知识储备不足。</span></p><hr /><p>&nbsp;</p><p>&nbsp;</p><h2 id='1linux指令'><span>1、Linux指令</span></h2><h4 id='1-rm---rf--的作用'><span>1 </span><code>rm  -rf /</code><span> 的作用？</span></h4><p><span>为了回答这个问题，需要先清楚一些概念。</span></p><ul><li><p><span>Shell是什么， shell是一个命令行的用户界面 执行linux指令，早期程序员没有图形界面用，就用 Shell。因为Linux指令也是一个个程序，shell负责解析和控制操作系统运行这些指令。</span></p></li><li><p><span>Linux的文件目录：树状抽象，/ 根目录，每一节用/分开。</span><code>pwd</code><span>（Print Working Directory）查看工作目录，</span><code>cd 切换到home目录， cd . 什么都不发生.代表当前目录；cd.. 退回上一级目录</code><span> </span></p></li><li><p><span>常见的文件类型：</span></p><ol start='' ><li><span>普通文件（比如一个文本文件）；</span></li></ol></li></ul><ol start='2' ><li><span>目录文件（目录也是一个特殊的文件，它用来存储文件清单，比如/也是一个文件）；</span></li><li><span>可执行文件（上面的rm就是一个可执行文件）* 结尾</span></li><li><span>管道文件  以| 结尾</span></li><li><span>Socket 文件（网络插座，是客户端和服务器之间同步数据的接口）；= 结尾</span></li><li><span>软链接文件（相当于指向另一个文件所在路径的符号）；</span></li><li><span>硬链接文件（相当于指向另一个文件的指针）</span></li></ol><ul><li><p><strong><span>文件的增删查改</span></strong></p></li><li><p><span>增加： </span><code>touch 添加文本</code><span> ， </span><code>mkdir 添加路径</code><span> </span></p></li><li><p><span>查看：</span><code>ls 打印当前目录文件， ls -l更详细</code></p></li><li><p><span>删除： </span><code>rm 删除单个文件</code><span>， 删除一个目录：目录，不可以删除。因此我们需要增加一个可选项，比如-r即 recursive（递归）。目录是一个递归结构，所以需要用递归删除。最后，你会发现</span><code>rm hello -r</code><span>删除了hello目录。</span></p></li><li><p><span>修改： vi  或nano都是自带的编辑器，vi简单的指令：</span><code>i 输入模式，保存并退出 esc + :wq</code><span>， </span><code>复制一行： esc退出i模式，yy指令复制一行，3yy 3行， dd剪切， p粘贴</code></p></li><li><p><span>查看文件： </span><code>cat， 文件过大会占用太多资源的情况用more</code><span> , </span></p></li><li><p><code>grep</code><span> : g：global 全局， re regular expression， p patern.所以这个指令的作用是通过正则表达式全局搜索一个文件找到匹配的模式.  比如命令：</span><code>grep print ./foo.py</code><span>查找该文件的print函数。</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220428133324947.png" alt="image-20220428133324947" style="zoom: 67%;" /></p></li><li><p><code>find</code><span> 查找文件,  比如我们如果想要查找所有.txt 扩展名的文件，使用</span><code>find / -iname &quot;*.txt&quot;</code><span>，</span><code>-iname</code><span>这个参数是用来匹配查找的，i 字母代表忽略大小写，这里也可以用-name替代。</span></p></li><li><p><strong><span>开头问题的回答：</span></strong><span> </span></p><p><code>/</code><span>是文件系统根目录；</span></p><p><code>rm</code><span>是删除指令；</span></p><p><code>-r</code><span>是 recursive（递归）；</span></p><p><code>-f</code><span>是 force（强制），遇到只读文件也不提示，直接删除。</span></p><p><span>所以</span><code>rm -rf /</code><span>就是删除整个文件系统上的所有文件，而且不用给用户提示。</span></p></li><li><p><span>搜索文件系统中所有以包含std字符串且以.h扩展名结尾的文件。</span><code>find / -iname “*std*.h”, * 匹配首尾模糊搜索</code><span> </span></p></li></ul><p>&nbsp;</p><h4 id='2-进程重定向和管道指令xargs-指令的作用是'><span>2 进程、重定向和管道指令：xargs 指令的作用是？</span></h4><ul><li><p><span>进程 :应用的可执行文件是放在文件系统里，把可执行文件启动，就会在操作系统里（具体来说是内存中）形成一个应用的副本，这个副本就是进程.  </span><u><span>进程是应用的执行副本</span></u><span>；而不要回答进程是操作系统分配资源的最小单位。前者是定义，后者是作用. </span></p></li><li><p><code>ps</code><span> 显示当前线程快照， </span><code>ps -ef</code><span>显示进程详情</span></p></li><li><p><code>top, htop</code><span> 更详细的进程</span></p></li><li><p><span>管道（Pipeline）作用是在命令和命令之间，传递数据。将一个进程的输出流定向到另一个进程的输入流，就像水管一样，作用就是把这两个文件接起来。如果一个进程输出了一个字符 X，那么另一个进程就会获得 X 这个输入。</span></p></li><li><p><span>重定向： 执行一个指令，比如</span><code>ls -l</code><span>，结果会写入标准输出流，进而被打印。这时可以用重定向符将结果重定向到一个文件，比如说 </span><code>ls -l &gt; out</code><span>，这样out文件就会有ls -l的结果；而屏幕上也不会再打印ls -l的结果。</span><code>&gt; 覆盖重定向， &gt;&gt;追加重定向</code></p></li><li><p><span>应用： </span><strong><span>排序文件名</span></strong><span>，将ls的结果传递给sort指令去排序。这样ls的开发者就不用关心排序问题了。</span><code>ls | sort</code></p></li><li><p><span>去重：</span><code>uniq</code><span>指令能够找到文件中相邻的重复行，然后去重。但是我们上面的文件重复行是交替的，所以不可以直接用因此可以先sort这个文件，然后利用管道将sort的结果重定向到uniq指令。指令如下：</span><code>sort a.txt | uniq</code></p></li><li><p><span>筛选：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="bash"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="bash"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.4458px; left: 7.98461px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><span><span>​</span>x</span></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#find ./递归列出当前目录下所有目录中的文件。grep从find的输出流中找出含有Spring关键字的行。</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">find</span> ./ | <span class="cm-builtin">grep</span> Spring</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#希望包含Spring但不包含MyBatis就可以这样操作</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">find</span> ./ | <span class="cm-builtin">grep</span> Spring | <span class="cm-builtin">grep</span> <span class="cm-attribute">-v</span> MyBatis</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 160px;"></div><div class="CodeMirror-gutters" style="display: none; height: 160px;"></div></div></div></pre></li><li><p><span>统计行数：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.44615px; left: 7.98461px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#统计文件行数</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">wc</span> <span class="cm-attribute">-l</span> a.txt</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#统计目录下文件数</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">ls</span> | <span class="cm-builtin">wc</span> <span class="cm-attribute">-l</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#统计整个java项目多个文件的总代码行</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#先找到所有.java后缀 ，再用xargs 逐行计算</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">find</span> ./ <span class="cm-attribute">-iname</span> <span class="cm-string">"*.txt"</span> |xargs <span class="cm-builtin">wc</span> <span class="cm-attribute">-l</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 保存中间结果</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 这句指令的意思是从当前目录中找到所有含有 Spring 关键字的 Java 文件。</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># tee 本身不影响指令的执行，但是 tee 会把 find 指令的结果保存到 JavaList 文件中。</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">find</span> ./ <span class="cm-attribute">-iname</span> <span class="cm-string">"*.java"</span> | <span class="cm-builtin">tee</span> JavaList | <span class="cm-builtin">grep</span> Spring</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 343px;"></div><div class="CodeMirror-gutters" style="display: none; height: 343px;"></div></div></div></pre></li><li><p><strong><span>xargs:</span></strong><span> 指令从标准数据流中构造并执行一行行的指令。xargs从输入流获取字符串，然后利用空白、换行符等切割字符串，在这些字符串的基础上构造指令，最后一行行执行这些指令。 </span></p></li><li><p><span>上面的</span><strong><span>题目解析</span></strong><span>：  xargs 将标准输入流中的字符串分割成一条条子字符串，然后再按照我们自己想要的方式构建成一条条指令，大大拓展了 Linux 指令的能力。</span></p><p><span>比如我们可以用来按照某种特定的方式逐个处理一个目录下所有的文件；根据一个 IP 地址列表逐个 ping 这些 IP，收集到每个 IP 地址的延迟等。</span></p></li></ul><p>&nbsp;</p><h4 id='3--用户和权限管理指令-linux-权限划分的原则'><span>3  用户和权限管理指令： Linux 权限划分的原则</span></h4><ul><li><p><span>Linux 遵循最小权限原则。</span></p><ul><li><span>每个用户掌握的权限应该足够小，每个组掌握的权限也足够小。实际生产过程中，最好管理员权限可以拆分，互相牵制防止问题。</span></li><li><span>每个应用应当尽可能小的使用权限。最理想的是每个应用单独占用一个容器（比如 Docker），这样就不存在互相影响的问题。即便应用被攻破，也无法攻破 Docker 的保护层。</span></li><li><span>尽可能少的root。如果一个用户需要root能力，那么应当进行权限包围——马上提升权限（比如 sudo），处理后马上释放权限。</span></li><li><span>系统层面实现权限分级保护，将系统的权限分成一个个 Ring，外层 Ring 调用内层 Ring 时需要内层 Ring 进行权限校验。</span></li></ul></li></ul><p>&nbsp;</p><h4 id='4-linux-中的网络指令'><span>4 Linux 中的网络指令</span></h4><ul><li><span>远程操作： </span><code>ssh</code><span> 指令允许远程登录到目标计算机并进行远程操作和管理. </span><code>scp</code><span> 远程传送文件.</span></li><li><span>查看本地网络状态:  </span><code>ifconfig</code><span> : 想知道本地ip以及本地有哪些网络接口时，就可以使用ifconfig指令。你可以把一个网络接口理解成一个网卡，有时候虚拟机会装虚拟网卡，虚拟网卡是用软件模拟的网卡。</span></li><li><code>netstat</code><span> : 另一个查看网络状态的场景是想看目前本机的网络使用情况</span></li><li><span>查看 TCP 连接 : </span><code>netstat -t</code></li><li><span>网络测试， 测试延迟 </span><code>ping</code><span>, </span></li><li><code>telnet</code><span>:  本机到某个 IP + 端口的网络是否通畅. </span></li><li><span>DNS 查询， </span><code>host + 网址</code><span>, </span><code>host -t 查找指定类型</code></li><li><span>HTTP相关： </span><code>curl</code><span> 如果要在命令行请求一个网页，或者请求一个接口，可以用curl指令。curl支持很多种协议，比如 LDAP、SMTP、FTP、HTTP 等。</span></li></ul><p>&nbsp;</p><h4 id='5-利用-linux-指令分析-web-日志'><span>5 利用 Linux 指令分析 Web 日志</span></h4><ul><li><p><span>htop 分析当前机器负载和日志大小，确保有资源做计算</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="bash" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="bash"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.44615px; left: 7.98461px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#下载日志</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">wget</span> https://github.com/ramroll/lagou-os/blob/main/access.log</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#查看大小</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">ls</span> <span class="cm-attribute">-l</span> access.log <span class="cm-attribute">--block-size</span><span class="cm-operator">=</span>M</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># less 或者head 查看部分</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#access_log每一行都是一次用户的访问，从左到右依次是：</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#IP 地址；</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#时间；</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#HTTP 请求的方法、路径和协议版本、返回的状态码；</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">#User Agent。</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># PV 分析</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">&gt;wc <span class="cm-attribute">-l</span> access.log </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-number">51462</span> access.log</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># pv分组</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># awk是一个处理文本的领域专有语言</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># $4 文本第四行 时间</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">awk</span> <span class="cm-string">'{print $4}'</span> access.log&nbsp; | less</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">[17/May/2015:08:05:06</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">[17/May/2015:08:05:55</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 利用 awk提供的字符串截取的能力</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 数字2代表从第 2 个字符开始，数字11代表截取 11 个字符</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">&gt; <span class="cm-builtin">awk</span> <span class="cm-string">'{print substr($4, 2, 11)}'</span> access.log&nbsp; | head <span class="cm-attribute">-n</span> <span class="cm-number">10</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 接下来我们就可以分组统计每天的日志条数了</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">&gt; <span class="cm-builtin">awk</span> <span class="cm-string">'{print substr($4, 2, 11)}'</span> access.log&nbsp; | <span class="cm-builtin">sort</span> | uniq <span class="cm-attribute">-c</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 分析UV, Y用IP近似UV</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">&gt; <span class="cm-builtin">awk</span> <span class="cm-string">'{print $1}'</span> access.log | <span class="cm-builtin">sort</span> | uniq | <span class="cm-builtin">wc</span> <span class="cm-attribute">-l</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 分组UV</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 第一次awk我们将第 4 列的日期和第 1 列的ip地址拼接在一起。</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 下面的sort是把整个文件进行一次字典序排序，相当于先根据日期排# 序，再根据 IP 排序。</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 接下来我们用uniq去重，日期 +IP 相同的行就只保留一个。</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 最后的awk我们再根据第 1 列的时间和第 2 列的 IP 进行统计</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">#!/usr/bin/bash</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">awk</span> <span class="cm-string">'{print substr($4, 2, 11) " " $1}'</span> access.log |\</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-tab" role="presentation" cm-text="	">    </span><span class="cm-builtin">sort</span> | uniq |\</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-tab" role="presentation" cm-text="	">    </span><span class="cm-builtin">awk</span> <span class="cm-string">'{uv[$1]++;next}END{for (ip in uv) print ip, uv[ip]}'</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 1007px;"></div><div class="CodeMirror-gutters" style="display: none; height: 1007px;"></div></div></div></pre><p>&nbsp;</p></li></ul><hr /><p>&nbsp;</p><h2 id='2组成原理'><span>2、组成原理</span></h2><h4 id='6-问题的可计算性'><span>6 问题的可计算性</span></h4><ul><li><span>可计算理论，图灵机：计算或者程序可以用来做什么？比如：计算可不可以用来做饭？换一个更专业的说法，做饭可不可以被计算？</span></li><li><span>公理化体系和不完备性定理：</span><u><span>希尔伯特</span></u><span>：这个世界可以建立一套完善的公理体系，由少数几个公理出发，推导出所有的定理和推论。这样就可以逐渐通过这种方法将世界上的万事万物都统一到一个体系中。</span><u><span>哥德尔不完备性定理</span></u><span>，内容是：即便在完善的公理体系中仍然可以找到不能被证明也不能被证伪的命题。</span></li><li><span>可计算理论：哪些问题可以被计算，哪些不可以被计算，这就是可计算性理论，该理论是计算机科学的理论基础之一。</span></li><li><span>停机问题：我们也无法实现用一个通用程序去判断另一个程序是否会停止。比如你用运行这段程序来检查一个程序是否会停止时，你会发现不能因为这个程序执行了 1 天，就判定它不会停止，也不能因为这个程序执行了 10 年，从而得出它不会停止的结论。</span></li><li><span>多项式时间（ Polynomial time）问题。时间复杂度能以多项式表达出来的，在当前计算能力或随着计算能力发展能解决的问题。一些比如O(2^N)的问题随这类型的问题随着规模 N 上升，时间开销的增长速度和人类计算能力增长速度持平甚至更快。因此虽然这类问题可以计算，但是当 N 较大时，因为计算能力不足，最终结果依然无法被解决。</span></li><li><span>NP 问题： 不是所有可以计算的问题都可以被解决，问题如果不能在多项式时间内找到答案的记为NP.</span></li></ul><h4 id='7-相比-32-位64-位的优势是什么'><span>7 相比 32 位，64 位的优势是什么? </span></h4><ul><li><p><span>冯诺依曼结构回顾：</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220429232843002.png" referrerpolicy="no-referrer" alt="image-20220429232843002"></p></li><li><p><span>内存：程序和数据被存储的地方，线性排列，最小储存单位为字节，一个字节都对应一个内存地址，每个地址的寻址时间相同。</span></p></li><li><p><span>CPU: 负责控制指令的执行和具体的计算。如果 CPU 每次可以计算 4 个 byte，那么我们称作 32 位 CPU，计算 8 个 byte，那么我们称作 64 位 CPU。</span></p></li><li><p><span>寄存器： CPU内储存数据，数据量很小 速度很快</span></p></li><li><p><span>总线： CPU 和内存以及其他设备之间，也需要通信，因此我们用一种特殊的设备进行控制。</span><strong><span>地址总线</span></strong><span>，专门用来指定 CPU 将要操作的内存地址。 </span><strong><span>数据总线</span></strong><span>，用来读写内存中的数据，</span><strong><span>控制总线</span></strong><span>，用来发送和接收关键信号，比如中断信号。</span></p></li><li><p><span>32 位宽的 CPU 没办法控制超过 32 位的地址总线、数据总线工作。比如说你有一条 40 位的地址总线（其实就是 40 条线），32 位的 CPU 没有办法一次给 40 个信号，因为它最多只有 32 位的寄存器。因此 32 位宽的 CPU 最多操作 232 个内存地址，也就是 4G 内存地址。</span></p></li><li><p><span>CPU 执行指令的过程：</span></p><ul><li><span>首先 CPU 通过 PC 指针读取对应内存地址的指令，我们将这个步骤叫作 Fetch，就是获取的意思。</span></li><li><span>CPU 对指令进行解码，我们将这个部分叫作 Decode。</span></li><li><span>CPU 执行指令，我们将这个部分叫作 Execution。</span></li><li><span>CPU 将结果存回寄存器或者将寄存器存入内存，我们将这个步骤叫作 Store。</span></li></ul></li></ul><p>&nbsp;</p><p>&nbsp;</p><hr /><p>&nbsp;</p><p>&nbsp;</p><h2 id='3基础知识'><span>3、基础知识</span></h2><h4 id='8-linux-内核和-windows-内核'><span>8 Linux 内核和 Windows 内核</span></h4><ul><li><p><span>什么是内核：内核是操作系统中应用连接硬件设备的桥梁。至少提供以下功能：</span></p><ul><li><span>管理进程、线程（决定哪个进程、线程使用 CPU）</span></li><li><span>管理内存（决定内存用来做什么）</span></li><li><span>连接硬件设备（为进程、和设备间提供通信能力）</span></li><li><span>提供系统调用（接收进程发送来的系统调用）</span></li></ul></li><li><p><span>内核的权限很高，可以直接操作内存，所以和进程之间有隔离。进程向内核发起请求，内核响应进程的请求。进程向内核发起一个请求，然后将 CPU 执行权限让出给内核。内核接手 CPU 执行权限，然后完成请求，再转让出 CPU 执行权限给调用进程。</span></p></li><li><p><span>宏内核(Monolithic Kernel)：Linux 是宏内核架构，这说明 Linux 的内核是一个完整的可执行程序，且内核用最高权限来运行。宏内核的特点就是有很多程序会打包在内核中，比如，文件系统、驱动、内存管理等。</span></p></li><li><p><span>微内核，内核只保留最基本的能力。比如进程调度、虚拟内存、中断。多数应用，甚至包括驱动程序、文件系统，是在用户空间管理的.</span></p></li><li><p><span>混合类型内核。 架构像微内核，内核中会有一个最小版本的内核，其他功能会在这个能力上搭建。实现的时候，是用宏内核的方式实现.</span></p></li><li><p><span>Linux 是宏内核，NT 内核属于混合型内核. 另外 NT 内核和 Linux 内核还存在着许多其他的差异，比如：</span></p><p><span>Linux 内核是一个开源的内核；</span></p><p><span>它们支持的可执行文件格式不同；</span></p><p><span>它们用到的虚拟化技术不同。</span></p></li></ul><p>&nbsp;</p><h4 id='9-用户态线程和内核态线程有什么区别'><span>9 用户态线程和内核态线程有什么区别？</span></h4><ul><li><p><span>内核运行有最高权限，多数应用程序运行在需要的最小权限下，因此操作系统将内存分为两个区域，内核空间(Kernel Space)和用户空间。</span></p></li><li><p><span>用户态（User Mode） 用户空间中的代码被限制了只能使用一个局部的内存空间。</span></p></li><li><p><span>内核态（Kernel Mode） 程序执行在内核空间，代码可以访问所有内存。</span></p></li><li><p><span>程序在现代操作系统中并不是以进程为单位在执行，而是以一种轻量级进程， 即线</span><strong><span>程（Thread）</span></strong><span>的形式执行。一个进程有多个线程。进程创建的时候，一有一个主线程随着进程创建而创建。</span></p></li><li><p><span>用户态线程User Level Thread：内核并不知道它的存在，它完全是在用户空间中创建</span></p><ul><li><span>优势：开销小：创建、销毁不需要系统调用；切换成本低：用户空间程序可以自己维护。</span></li><li><span>缺点：与内核协作成本高；线程间协作成本高，两个线程要通信要调用系统I/O; 无法利用多核，操作系统直接调用的还是整个进程，无论都是线程，都只能并发执行一个线程。</span></li></ul></li><li><p><span>内核态线程： Kernel Level Thread， 执行在内核态。</span></p><ul><li><span>优势：可以利用多核 CPU 优势；操作系统级优化：内核中的线程操作 I/O 不需要进行系统调用；一个内核线程阻塞了，可以立即让另一个执行。</span></li><li><span>缺点：创建成本高；扩展性差；切换成本较高。</span></li></ul></li><li><p><span>用户态线程和内核态线程之间的映射关系：针对用户态线程不能利用多核的缺点，在内核中预先创建一些线程，并反复利用这些线程，因此有4种对应关系。</span></p><ul><li><span>多对一（Many to One）用户态进程中的多线程复用一个内核态线程。减少了创建内核态线程的成本，但是线程不可以并发。</span></li><li><span>一对一（One to One）：每个用户态的线程分配一个单独的内核态线程，Windows NT的模式</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220430115239055.png" alt="image-20220430115239055" style="zoom:33%;" /><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220430115344167.png" alt="image-20220430115344167" style="zoom:33%;" /></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220504212845943.png" alt="image-20220504212845943" style="zoom:33%;" /></li><li><span>多对多（Many To Many） n 个用户态线程分配 m 个内核态线程。m 通常可以小于 n，减少了内核线程，同时也保证了多核心并发，Linux的模式。</span></li><li><span>两层设计（Two Level）模型混合了多对多和一对一的特点。多数用户态线程和内核线程是 n 对 m 的关系，少量用户线程可以指定成 1 对 1 的关系。</span></li></ul></li></ul><p>&nbsp;</p><hr /><p>&nbsp;</p><p>&nbsp;</p><h2 id='4线程和进程'><span>4、线程和进程</span></h2><h4 id='10-进程的开销比线程大在了哪里'><span>10 进程的开销比线程大在了哪里</span></h4><ul><li><p><strong><span>进程（Process）</span></strong><span>就是正在执行的应用程序，是软件的执行副本。而线程是轻量级的进程。进程是分配资源的基础单位。而</span><strong><span>线程</span></strong><span>很长一段时间被称作轻量级进程（Light Weighted Process），是程序执行的基本单位。进程和线程是操作系统为了分配资源设计的两个概念，进程承接存储资源，线程承接计算资源。而进程包含线程，这样就可以做到进程间内存隔离。</span></p><ul><li><span>并发性，任何进程都可以和其他进程一起运行。</span></li><li><span>动态性，进程是正在执行程序的实例，动态产生动态消亡，在3种状态之间转化。</span></li><li><span>独立性，资源分配的独立单位</span></li><li><span>交互性，进程之间需要通信，交换数据</span></li><li><span>异步性，进程之间以相对独立，不一定相同的速度向前推进</span></li><li><span>线程：进程的子集，多个线程共享进程的内存等资源</span></li><li><span>进程有独立的地址空间，线程共享地址空间。</span></li><li><span>Context switch： 发生在线程时速度比进程快。</span></li></ul></li><li><p><span>操作系统需要思考分配资源。最重要的 3 种资源是：计算资源（CPU）、内存资源和文件资源.  为线程分配时间段，每个线程执行一小段时间，依次执行。</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220501211935108.png" alt="image-20220501211935108" style="zoom:50%;" /></p></li></ul><p>&nbsp;</p><ul><li><span>进程建立后的几个状态，创建成功，就绪(ready)状态，开始执行 运行(running), 运行完当前时间片段 回到ready，或者没有完成等待调度器执行下一个阶段(waiting)。等待系统响应读取数据等 阻塞(block)状态, 进程完成，结束状态(terminated)。</span></li><li><span>进程需要一个表来储存自身的信息，内存位置、大小， PID 是多少、当前状态。。OS中同时有很多进程，需要为它们分配完全隔离的内存区域，即便进程内部程序读取了相同地址，而实际的物理地址也不会相同，这就好比 A 小区的 10 号楼 808 和 B 小区的 10 号楼 808 不是一套房子，这种方法叫作地址空间。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220501213358570.png" alt="image-20220501213358570" style="zoom:33%;" /></li></ul><p>&nbsp;</p><ul><li><span>进程（线程）切换：因为操作系统提供了保存、恢复进程状态的能力，使得进程（线程）也可以在多个核心之间切换。</span><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220501214050270.png" alt="image-20220501214050270" style="zoom:50%;" /></li></ul><p>&nbsp;</p><ul><li><span>创建进程（线程）的 API， fork() 命令会创建一个克隆的进程。</span></li><li><strong><span>开始的问题 进程的开销比线程大在了哪里</span></strong><span>： Linux 中创建一个进程自然会创建一个线程，也就是主线程。创建进程需要为进程划分出一块完整的内存空间，有大量的初始化操作，比如要把内存分段（堆栈、正文区等）。创建线程则简单得多，只需要确定 PC 指针和寄存器的值，并且给线程分配一个栈用于执行程序，同一个进程的多个线程间可以复用堆栈。因此，创建进程比创建线程慢，而且进程的内存开销更大。</span></li></ul><p>&nbsp;</p><h4 id='11-锁信号量和分布式锁'><span>11 锁、信号量和分布式锁</span></h4><ul><li><p><span>原子操作: 操作不可分的操作。</span><strong><span>竞争条件</span></strong><span>就是说多个线程对一个资源（内存地址）的读写存在竞争，在这种条件下，最后这个资源的值不可预测，而是取决于竞争时具体的执行顺序。</span></p></li><li><p><strong><span>临界区</span></strong><span>(critical section)各个进程中对某个临界资源（一次只允许一个进程使用）实施操作的程序片段。</span></p></li><li><p><span>比如</span><code>i++</code><span>就不是一个原子操作，因为它是 3 个原子操作组合而成的：</span></p><ul><li><span>读取 </span><code>i</code><span> 的值；</span></li><li><span>计算 </span><code>i+1</code><span>；</span></li><li><span>写入新的值。</span></li><li><span>像这样的操作，在多线程 + 多核环境会造成竞争条件。</span></li></ul></li><li><p><span>解决竞争条件：互斥，即不要让程序同时进入临界区；避免竞争条件，比如 ThreadLocal、 cas 指令。</span></p></li><li><p><span>cas 指令(Compare And Swap)： </span><strong><span>比较并交换</span></strong><span>，是原子操作的一种，可用于在多线程编程中实现不被打断的数据交换操作，从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。 该操作通过将内存中的值与指定数据进行比较，当数值一样时将内存中的数据替换为新的值。在使用上，通常会记录下某块内存中的</span><em><span>旧值</span></em><span>，通过对</span><em><span>旧值</span></em><span>进行一系列的操作后得到</span><em><span>新值</span></em><span>，然后通过CAS操作将</span><em><span>新值</span></em><span>与</span><em><span>旧值</span></em><span>进行交换。如果这块内存的值在这期间内</span><u><span>没被修改过，则</span><em><span>旧值</span></em><span>会与内存中的数据相同</span></u><span>，这时CAS操作将会成功执行 使内存中的数据变为</span><em><span>新值</span></em><span>。如果内存中的值在这期间内被修改过，则一般来说</span><em><span>旧值</span></em><span>会与内存中的数据不同，这时CAS操作将会失败，</span><em><span>新值</span></em><span>将不会被写入内存。</span></p></li><li><p><strong><span>锁（lock）</span></strong><span>，目标是实现抢占（preempt）。就是只让给定数量的线程进入临界区。锁可以用tas或者cas来实现。</span></p></li><li><p><strong><span>生产者消费者模型</span></strong><span>, 一个或多个生产者将数据放置在缓冲区，消费者从缓冲区读取数据，每次读取一项。每次只有一个生产者或者消费者对缓冲区进行操作。比如： wait 是一个生产者，将当前线程挂到一个等待队列上，并休眠。notify 是一个消费者，从等待队列中取出一个线程，并重新排队。</span></p></li><li><p><span>如何控制同一时间只有 2 个线程运行？</span></p><ul><li><p><span>【解析】 同时控制两个线程进入临界区，一种方式可以考虑用信号量（semaphore）。</span></p><p><span>另一种方式是考虑生产者、消费者模型。想要进入临界区的线程先在一个等待队列中等待，然后由消费者每次消费两个。这种实现方式，类似于实现一个线程池，所以也可以考虑实现一个 ThreadPool 类，然后再实现一个调度器类，最后实现一个每次选择两个线程执行的调度算法。</span></p></li></ul></li></ul><p>&nbsp;</p><h4 id='12-乐观锁区块链除了上锁还有哪些并发控制方法'><span>12 乐观锁、区块链：除了上锁还有哪些并发控制方法？</span></h4><ul><li><p><span>并发场景，设计系统的目的往往是达到同步（Synchronized）的状态，同步就是大家最终对数据的理解达成了一致。</span></p></li><li><p><span>同步的一种方式，就是让</span><strong><span>临界区互斥</span></strong><span>。 这种方式，每次只有一个线程可以进入临界区. 让临界区互斥的方法（对临界区上锁），具有强烈的排他性，对修改持保守态度，我们称为</span><strong><span>悲观锁（Pressimistic Lock）。</span></strong><span> 比如mysql的行锁、读锁。</span></p></li><li><p><strong><span>乐观锁（Optimistic Lock）</span></strong><span>。你每天都用的，基于乐观锁的应用就是版本控制工具 Git。Git 允许大家一起编辑，将结果先存在本地，然后都可以向远程仓库提交，如果没有版本冲突，就可以提交上去。这就是一种典型的乐观锁的场景，或者称为基于版本控制的场景。(回忆提交git的场景，先更新的人被采纳，后更新的人负责解决冲突)</span></p></li><li><p><strong><span>ThreadLocal</span></strong><span>，让每个线程访问不同的资源，旨在用空间换时间，也是避免锁的一种方案。</span></p><p>&nbsp;</p></li></ul><h4 id='13-线程调度都有哪些方法'><span>13 线程调度都有哪些方法</span></h4><ul><li><span>所谓调度，是一个制定计划的过程，就是操作系统如何决定未来执行哪些线程。</span></li><li><span>最朴素的算法：先到先服务（First Come First Service，FCFS），公平且没有额外开销。缺点：一个先到的服务需要运行1小时，而后到的只需要1秒，却要等一个小时后才能执行。</span></li><li><span>短作业优先（Shortest Job First，SJF）同时考虑到来顺序和作业预估时间的长短，在大多数情况下，应该优先处理用时少的，从而降低平均等待时长。</span></li><li><span>还要没解决的问题：紧急任务如何插队？等太久的任务如何插队？</span></li><li><span>优先级队列（Priority Queue），给每个任务分配一个优先级，用Heap数据结构实现O(1)时间内返回优先度最大的。这样紧急任务就可以得到最大优先级先执行。</span></li><li><span>抢占（preemption）抢占就是把执行能力分成时间片段，让每个任务都执行一个时间片段。如果在时间片段内，任务完成，那么就调度下一个任务。如果任务没有执行完成，则中断任务，让任务重新排队，调度下一个任务。这样高优先级的任务可以用非抢占+优先级队列，这样可以得到先执行，且大时间片段执行。</span></li><li><span>总结：紧急任务仍然走高优队列，非抢占执行。普通任务先放到优先级仅次于高优任务的队列中，并且只分配很小的时间片；如果没有执行完成，说明任务不是很短，就将任务下调一层。下面一层，最低优先级的队列中时间片很大，长任务就有更大的时间片可以用。</span><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220504170953692.png" referrerpolicy="no-referrer" alt="image-20220504170953692"></li></ul><p>&nbsp;</p><p>&nbsp;</p><h4 id='14-饥饿和死锁'><span>14 饥饿和死锁</span></h4><ul><li><p><span>死锁是线程间互相等待资源，但是没有一个线程可以进行下一步操作.</span></p></li><li><p><span>饥饿（Starvation）就是因为某种原因导致线程得不到需要的资源，无法继续工作。死锁是饥饿的一种形式，因为循环等待无法得到资源。哲学家就餐问题，会形成一种环状的死锁（循环依赖）， 因此非常具有代表性。</span></p></li><li><p><span>哲学家就餐问题：有 5 个哲学家，围着一个圆桌就餐。圆桌上有 5 份意大利面和 5 份叉子。哲学家比较笨，他们必须拿到左手和右手的 2 个叉子才能吃面。哲学不饿的时候就在思考，饿了就去吃面，吃面的必须前提是拿到 2 个叉子，吃完面哲学家就去思考。</span></p></li><li><p><span>死锁有 4 个基本条件：</span></p><ul><li><span>资源存在互斥逻辑：每次只有一个线程可以抢占到资源（哲学家抢占叉子）</span></li><li><span>持有等待，这里哲学家会一直等待拿到叉子。</span></li><li><span>禁止抢占：如果拿不到资源一直会处于等待状态，而不会释放已经拥有的资源。</span></li><li><span>循环等待：这里哲学家们会循环等待彼此的叉子。</span></li></ul></li></ul><p>&nbsp;</p><p>&nbsp;</p><h4 id='15-进程间通信'><span>15 进程间通信</span></h4><ul><li><p><strong><span>进程间通信</span></strong><span>（Intermediate Process Communication，</span><strong><span>IPC</span></strong><span>）。所谓通信就是交换数据</span></p></li><li><p><strong><span>管道</span></strong><span>：不侵入、灵活，不会增加程序设计负担，又能组织复杂的计算过程。</span></p></li><li><p><span>本地内存共享，速度快，但是需要额外编程。（Linux 中有 POSIX 内存共享库—shmem）</span></p></li><li><p><strong><span>远程调用（Remote Procedure Call，RPC）</span></strong><span>：通过本地程序调用来封装远程服务请求的方法。比如 Hive用到的Thrift 框架（Facebook 开源）、Dubbo 框架（阿里开源）、grpc（Google 开源）。</span></p></li><li><p><span>程序员调用 RPC 的时候，程序看上去是在调用一个本地的方法，或者执行一个本地的任务，但是后面会有一个服务程序（通常称为 stub），将这种本地调用转换成远程网络请求。 同理，服务端接到请求后，也会有一个服务端程序（stub），将请求转换为一个真实的服务端方法调用。缺点：RPC 真正的缺陷是</span><u><span>增加了系统间的耦合</span></u><span>。当系统主动调用另一个系统的方法时，就意味着在增加两个系统的耦合。长期增加 RPC 调用，会让系统的边界逐渐腐化。</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220504174617074.png" alt="image-20220504174617074" style="zoom:50%;" /></p></li><li><p><span>上面这张图，表示客户端和服务端通信的过程，一共是 10 个步骤，分别是：</span></p><p><span>0 客户端调用函数（方法）；</span></p><p><span>1 stub 将函数调用封装为请求；</span></p><p><span>2 客户端 socket 发送请求，服务端 socket 接收请求；</span></p><p><span>3 服务端 stub 处理请求，将请求还原为函数调用；</span></p><p><span>4 执行服务端方法；</span></p><p><span>5 返回结果传给 stub；</span></p><p><span>6 stub 将返回结果封装为返回数据；</span></p><p><span>7 服务端 socket 发送返回数据，客户端 socket 接收返回数据；</span></p><p><span>8 客户端 socket 将数据传递给客户端 stub；</span></p><p><span>9 客户端 stub 把返回数据转义成函数返回值。</span></p></li><li><p><strong><span>消息队列</span></strong><span>：降低系统耦合的方法：将服务抽象层事件，事件不会增加耦合，如果一个系统订阅了另一个系统的事件，那么将来无论谁提供同类型的事件，自己都可以正常工作。系统依赖的不是另一个系统，而是某种事件。具体场景的消息队列有 Kafka，主打处理 feed；Rocket MQ 等主打分布式应用间通信（应用解耦）。</span></p></li></ul><p>&nbsp;</p><h4 id='16-分析服务的特性我的服务应该开多少个进程多少个线程'><span>16 分析服务的特性：我的服务应该开多少个进程、多少个线程？</span></h4><ul><li><span>计算密集型和 I/O 密集型。I/O 本质是对设备的读写，如果操作对 I/O 的依赖强，比如频繁的文件操作（写日志、读写数据库等）。</span></li><li><span>负载指标，linux下的</span><code>top</code><span>指令， </span><code>load average——平均负载</code><span>。 负载可以理解成某个时刻正在排队执行的进程数除以 CPU 核数。平均负载需要多次采样求平均值。 如果这个值大于1，说明 CPU 相当忙碌。</span></li><li><span>通信量（Traffic）：如果怀疑瓶颈发生在网络层面，或者想知道当前网络状况。可以查看</span><code>/proc/net/dev</code></li><li><span>衡量磁盘工作情况， </span><code>iotop指令</code></li><li><span>初步分析的思路：核心vs 线程数，1倍，2倍，3倍开始。如果发现整个过程中，瓶颈在 CPU，比如load average很高，那么可以考虑优化 I/O Wait，让 CPU 有更多时间计算。</span></li><li><span>压力测试</span></li></ul><p>&nbsp;</p><p>&nbsp;</p><hr /><p>&nbsp;</p><p>&nbsp;</p><h2 id='5内存管理'><span>5、内存管理</span></h2><h4 id='17-虚拟内存'><span>17 虚拟内存</span></h4><ul><li><p><span>虚拟化技术解决的问题：内存不够用，程序员需要小心翼翼避免多开，避免进程内存增长超过阈值。</span></p></li><li><p><span>交换（Swap）技术: 允许一部分进程使用内存，不使用内存的进程数据（完整的进程数据）先保存在磁盘上。缺点：进程来回分配找出碎片化；频繁切换问题。</span></p></li><li><p><span>虚拟内存：应用使用的是虚拟内存，操作系统管理虚拟内存和真实内存之间的映射。操作系统将虚拟内存分成整齐小块，每个小块称为一个</span><strong><span>页（Page）</span></strong></p></li><li><p><span>页（Page）和页表：操作系统将虚拟内存分块，每个小块称为一个页（Page）；真实内存也需要分块，每个小块我们称为一个 Frame。Page 到 Frame 的映射，需要一种叫作页表的结构。</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220505213053617.png" alt="image-20220505213053617" style="zoom:50%;" /></p></li><li><p><span>页表维护了虚拟地址到真实地址的映射。每次程序使用内存时，需要把虚拟内存地址换算成物理内存地址，换算过程分为以下 3 个步骤：</span></p><p><span>1 通过虚拟地址计算 Page 编号；</span></p><p><span>2 查页表，根据 Page 编号，找到 Frame 编号；</span></p><p><span>3 将虚拟地址换算成物理地址。</span></p></li><li><p><span>上面的过程发生在 CPU 中一个小型的设备——内存管理单元（Memory Management Unit， </span><strong><span>MMU）</span></strong></p></li><li><p><span>页表详细内容：页表中的每一项（页表条目）长什么样子呢？下图是一个页表格式的一个演示。Absent（“在”）位，是一个 bit。0 表示页的数据在磁盘中（不再内存中），1 表示在内存中；Protection（保护）字段可以实现成 3 个 bit，它决定页表用于读、写、执行。Reference（访问）位，代表这个页被读写过，这个记录对回收内存有帮助。Dirty（“脏”）位，代表页的内容被修改过，如果 Dirty =1，那么意味着页面必须回写到磁盘上才能置换（Swap)。Frame Number（Frame 编号），这个是真实内存的位置。用 Frame 编号乘以页大小，就可以得到 Frame 的基地址。</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220505213546591.png" alt="image-20220505213546591" style="zoom:50%;" /></p></li></ul><p>&nbsp;</p><h4 id='18-内存管理单元-什么情况下使用大内存分页'><span>18 内存管理单元： 什么情况下使用大内存分页？</span></h4><ul><li><p><span>内存管理单元（Memory Management Unit(MMU）负责完成虚拟地址到物理地址转换。在程序执行的时候，指令中的地址都是虚拟地址，虚拟地址会通过 MMU，MMU 会查询页表，计算出对应的 Frame Number，然后偏移量不变，组装成真实地址。然后 MMU 通过地址总线直接去访问内存。所以 MMU 承担了虚拟地址到物理地址的转换以及 CPU 对内存的操作这两件事情。</span></p></li><li><p><span>性能限制： MMU 从内存中查询页表，速度跟不上CPU执行指令的速度，因此，在 MMU 中往往还有一个微型的设备，叫作转置检测缓冲区（Translation Lookaside Buffer，TLB）。TLB 的作用就是根据输入的 Page Number，找到 Frame Number。</span></p></li><li><p><strong><span>TLB 失效（Miss）</span></strong><span>Page Number 在 TLB 没有找到。</span><strong><span>软失效（Soft Miss）</span></strong><span>，这种情况 Frame 还在内存中，只不过 TLB 缓存中没有。那么这个时候需要刷新 TLB 缓存。</span><strong><span>硬失效（Hard Miss)</span></strong><span>，这种情况下对应的 Frame 没有在内存中，需要从磁盘加载，非常耗时。</span></p></li><li><p><span>TLB 缓存的设计：TLB 的缓存行将 Page Number 映射到 Frame Number，通常有3种映射方案：</span></p><ul><li><span>全相联映射（Fully Associative Mapping）</span></li><li><span>直接映射（Direct Mapping）直接映射类似一种哈希函数的形式，给定一个内存地址，可以通过类似于哈希函数计算的形式，去计算它在哪一个缓存条目。因为每一个地址对应确定的映射，置换缓存不能实现。</span></li><li><span>n 路组相联映射（n-way Set-Associative Mapping）组相联映射允许一个虚拟页号（Page Number）映射到固定数量的 n 个位置</span></li></ul></li></ul><p>&nbsp;</p><h4 id='19-缓存置换算法-lru-实现的数据结构'><span>19 缓存置换算法： LRU 实现的数据结构</span></h4><ul><li></li></ul><p>&nbsp;</p><h4 id='20-内存回收上篇如何解决内存的循环引用问题'><span>20 内存回收上篇：如何解决内存的循环引用问题？</span></h4><ul><li></li></ul><p>&nbsp;</p><h4 id='21-内存回收下篇三色标记-清除算法'><span>21 内存回收下篇：三色标记-清除算法？</span></h4><ul><li></li></ul><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><hr /><p>&nbsp;</p><p>&nbsp;</p><h2 id='6文件系统'><span>6、文件系统</span></h2><p>&nbsp;</p><h4 id='22--linux-下的各个目录'><span>22  Linux 下的各个目录</span></h4><ul><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220506160750996.png" referrerpolicy="no-referrer" alt="image-20220506160750996"></li></ul><p>&nbsp;</p><h4 id='23-文件系统的底层实现fatntfs-和-ext3-有什么区别'><span>23 文件系统的底层实现：FAT、NTFS 和 Ext3 有什么区别</span></h4><ul><li><p><span>使用硬盘和使用内存有一个很大的区别，内存可以支持到字节级别的随机存取，而这种情况在硬盘中通常是不支持的，为了提高性能，通常会将物理存储（硬盘）划分成一个个小块，一次读/写一个块（Block）才是可行的方案。</span></p></li><li><p><span>文件系统（File System）：负责利用上面的硬盘抽象为用户可用的存储文件的空间。</span></p></li><li><p><span>FAT格式，FAT 表通过一种类似链表的结构描述了文件对应的块。上图中：文件 1 从位置 5 开始，这就代表文件 1 在硬盘上的第 1 个块的序号是 5 的块 。然后位置 5 的值是 2，代表文件 1 的下一个块的是序号 2 的块。顺着这条链路，我们可以找到 5 → 2 → 9 → 14 → 15 → -1。-1 代表结束，所以文件 1 的块是：5,2,9,14,15。同理，文件 2 的块是 3,8,12。缺点：太占用内存空间</span><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220506165349933.png" alt="image-20220506165349933" style="zoom: 67%;" /></p></li><li><p><strong><span>索引节点（index node）</span></strong><span>：为了改进 FAT 的容量限制问题，可以考虑为每个文件增加一个索引节点（inode）。这样，随着虚拟内存的使用，当文件导入内存的时候，先导入索引节点（inode），然后索引节点中有文件的全部信息，包括文件的属性和文件物理块的位置。</span></p></li><li><p><span>总结： inode 的设计在内存中创造了一棵树状结构，对文件、目录进行管理，并且索引到磁盘中的数据。这是一种经典的数据结构，这种思路会被数据库设计、网络资源管理、缓存设计反复利用。日志文件系统——日志结构简单、容易存储、按时间容易分块，这样的设计非常适合缓冲、批量写入和故障恢复。</span></p><p><span>现在我们很多分布式系统的设计也是基于日志，比如 MySQL 同步数据用 binlog，Redis 的 AOF，著名的分布式一致性算法 Paxos ，因此 Zookeeper 内部也在通过实现日志的一致性来实现分布式一致性。</span></p></li></ul><p>&nbsp;</p><p>&nbsp;</p><h4 id='24-数据库文件系统实例mysql-中-b-树和-b-树'><span>24 数据库文件系统实例：MySQL 中 B 树和 B+ 树</span></h4><ul><li><span>行存储和列存储: 表格的储存主要是行存储（Row Storage）和列存储（Column Storage）两种存储方式。行存储将表格看作一个个记录，每个记录是一行。以包含订单号、金额、下单时间 3 项的表为例，行存储如下图所示：在计算机中没有真正的行的概念。行存储本质就是数据一个接着一个排列，一行数据后面马上跟着另一行数据。如果订单表很大，一个磁盘块（Block）存不下，那么实际上就是每个块存储一定的行数。 行存储更新一行的操作，往往可以在一个块（Block）中进行。而查询数据，聚合数据（比如求 4 月份的订单数），往往需要跨块（Block）。因此，行存储</span><strong><span>优点</span></strong><span>：更新快、单条记录的数据集中，适合事务。但</span><strong><span>缺点</span></strong><span>也很明显，查询慢。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220507180733581.png" referrerpolicy="no-referrer" alt="image-20220507180733581"></li><li><strong><span>列存储</span></strong><span>（Column Storage），列存储中数据是一列一列存的。还以订单表为例，如下图所示：</span><strong><span>优势</span></strong><span>查询和聚合运算快</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220507181351833.png" referrerpolicy="no-referrer" alt="image-20220507181351833"></li><li><strong><span>索引</span></strong><span>：</span><em><span>索引</span></em><span>是一组按一个或多个键的值以逻辑方式进行</span><strong><span>排序</span></strong><span>的指针， 目的：提高查询速度。因为数据有序了，可以使用二分查找等更快的算法查找数据。按照链表的方式储存索引：搜索O(log n), 插入O(n). 插入新元素的时候性能太低， 考虑到文件系统最终要写入低速的硬盘，情况更糟糕。</span></li><li><strong><span>树结构，B树，B+树</span></strong><span>：树的每个节点的数据分成 Key 和 Value。Key 就是索引值，比如订单 ID 创建索引，那么 Key 就是订单 ID。值中至少需要序号（对行存储也就是行号）。这样，如果们想找 18 对应的行，就可以先通过二叉搜索树找到对应的行号，然后再去对应的行读取数据。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220507220808382.png" alt="image-20220507220808382" style="zoom:67%;" /></li><li><span>二叉搜索树解决了连续结构插入新元素开销很大的问题，同时又保持着天然的二分结构。但是，当需要索引的数据量很大，无法在一个磁盘 Block 中存下整棵二叉搜索树的时候。每一次递归向下的搜索，实际都是读取不同的磁盘块。这个时候二叉搜索树的开销很大。解决方法：</span><strong><span>沿用树状结构，利用好磁盘的分块让每个节点多一些数据，并且允许子节点也多一些，树就会更矮。因为树的高度决定了搜索的次数。</span></strong></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220507222558111.png" referrerpolicy="no-referrer" alt="image-20220507222558111"><span>B 树中的父节点中的数据会对子树进行区段分割。比如上图中节点 1 有 3 个子节点，并用数字 9,30 对子树的区间进行了划分。上图中的 B 树是一个 3-4 B 树，3 指的是每个非叶子节点允许最大 3 个索引，4 指的是每个节点最多允许 4 个子节点，4 也指每个叶子节点可以存 4 个索引。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220507222715613.png" referrerpolicy="no-referrer" alt="image-20220507222715613"><span>B+ 树只有叶子节点才映射数据，可以自动平衡。上图中是对 B 树设计的一种改进，节点 1 为冗余节点，它不存储数据，只划定子树数据的范围。你可以看到节点 1 的索引 Key：12 和 30，在节点 3 和 4 中也有一份。B+ 树的插入和删除效率更高。B+ 树所有叶子节点间还有一个链表进行连接。这种设计对范围查找非常有帮助。</span></li></ul><p>&nbsp;</p><p>&nbsp;</p><hr /><p>&nbsp;</p><h2 id='7虚拟化技术'><span>7、虚拟化技术</span></h2><p>&nbsp;</p><h4 id='26-虚拟化技术-vmware-和-docker-的区别'><span>26 虚拟化技术 VMware 和 Docker 的区别？ </span></h4><ul><li><span>VMware 和 Docker 是目前虚拟化技术中最具代表性的两种设计。VMware 为应用提供虚拟的计算机</span><strong><span>（虚拟机）</span></strong><span>；Docker 为应用提供虚拟的空间，被称作</span><strong><span>容器（Container）</span></strong></li><li><span>虚拟机的设计: 满足三个条件：隔离、仿真、高效。</span></li><li><span>隔离（isolation），指的是一台实体机上的所有的虚拟机实例不能互相影响。这也是早期设计虚拟机的一大动力，比如可以在一台实体机器上同时安装 Linux、Unix、Windows、MacOS 四种操作系统</span></li><li><span>仿真（Simulation）指的是用起来像一台真的机器那样，包括开机、关机，以及各种各样的硬件设备。在虚拟机上执行的操作系统认为自己就是在实体机上执行。仿真主要的贡献是让进程可以无缝的迁移，也就是让虚拟机中执行的进程，真实地感受到和在实体机上执行是一样的</span></li><li><span>高效（Efficient）的目标是减少虚拟机对 CPU、对硬件资源的占用。</span></li><li><strong><span>Type 1虚拟机</span></strong><span>，在 Type-1 虚拟机中，Hypervisor 一方面作为操作系统管理硬件，另一方面作为虚拟机的管理程序。在 Hypervisor 之上创建多个虚拟机，每个虚拟机可以拥有不同的操作系统（Guest OS）。（虚拟机管理程序（Virtual Machine Monitor，VMM）称为 Hypervisor）</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220512214635656.png" alt="image-20220512214635656" style="zoom:67%;" /></li><li><span>Type-2 虚拟机 如下图，虚拟机本身也作为一个进程。它和操作系统中执行的其他进程并没有太大的区别。但是为了提升性能，有一部分 Hypervisor 程序会作为内核中的驱动执行。当虚拟机操作系统（Guest OS）执行程序的时候，会通过 Hypervisor 实现世界切换。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220512214725440.png" alt="image-20220512214725440" style="zoom:67%;" /></li></ul><ul><li><p><strong><span>容器（Container）</span></strong></p><p><span>虚拟机虚拟的是计算机，容器虚拟的是执行环境。每个容器都是一套独立的执行环境，如下图所示，容器直接被管理在操作系统之内，并不需要一个虚拟机监控程序。</span><strong><span>和虚拟机有一个最大的区别就是：容器是直接跑在操作系统之上的，容器内部是应用，应用执行起来就是进程。</span></strong><span>容器虽然达到了虚拟机同样的隔离性，创建、销毁、维护成本都更低，但是从安全性考虑，还是要优先选用虚拟机执行操作系统。</span></p><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220512214819569.png" alt="image-20220512214819569" style="zoom:67%;" /></p></li><li><p><span>容器依赖操作系统的能力直接实现，比如：</span></p><ul><li><span>Linux 的 </span><strong><span>Cgroups（Linux Control Groups）能力</span></strong><span>，可以用来限制某组进程使用的 CPU 资源和内存资源，控制进程的资源能使用；</span></li><li><span>Linux 的 </span><strong><span>Namespace 能力，</span></strong><span>可以设置每个容器能看到能够使用的目录和文件。</span></li></ul></li></ul><h4 id='27-容器编排技术如何利用-k8s-和-docker-swarm-管理微服务'><span>27 容器编排技术：如何利用 K8s 和 Docker Swarm 管理微服务</span></h4><ul><li><span>Kubernetes（K8s）是一个 Google 开源的容器编排方案。K8s 通过集群管理容器。用户可以通过命令行、配置文件管理这个集群——从而编排容器。Master 节点是集群的管理者，Worker 是工作节点，容器就在 Worker 上工作，一个 Worker 的内部可以有很多个容器。</span></li></ul><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220512215358084.png" alt="image-20220512215358084" style="zoom:67%;" /></p><ul><li><p><span>单点架构，所有的 Worker 节点上必须安装 kubelet，它是节点的管理程序，负责在节点上管理容器。Pod 是 K8s 对容器的一个轻量级的封装，每个 Pod 有自己独立的、随机分配的 IP 地址。Pod 内部是容器，可以 1 个或多个容器。</span></p><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220512215503546.png" alt="image-20220512215503546" style="zoom:67%;" /></p></li></ul><ul><li><p><span>利用 K8s 和 Docker Swarm 管理微服务的步骤总结：</span></p><ul><li><span>制作容器镜像：我们就是要先制作容器，如果使用 Docker 作为容器，那就要写 DockerFile，然后生成容器镜像。</span></li></ul><ul><li><span>上传镜像：制作好容器之后，我们往往会将容器上传到容器的托管平台。很多公司内部有自己的容器托管平台，这样下载容器的速度会非常快。</span></li></ul><ul><li><span>搭建集群：再接下来，我们要搭建一个 K8s 或者 Docker Swarm 的集群，将节点添加进去。</span></li></ul><ul><li><span>添加微服务 Pod/Task：然后我们要在集群中添加 Pod 的或者 Task，可以通过命令行工具，也可以通过书写配置文件。</span></li></ul><ul><li><span>设置服务：为 Pod/Task 设置服务，之后都通过服务来访问容器内的应用。</span></li></ul></li></ul><p><span>   </span></p><hr /><p>&nbsp;</p><p>&nbsp;</p><h2 id='8网络和通信'><span>8、网络和通信</span></h2><h4 id='30-互联网协议群tcpip）多路复用是怎么回事'><span>30 互联网协议群（TCP/IP）：多路复用是怎么回事？</span></h4><ul><li><p><span>协议的分层: 所有网络通信的本质目标就是进程间通信。世界上有很多进程需要通信，我们要找到一种通用的，每个进程都能认可和接受的通信方式，这就是协议。</span></p></li><li><p><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220510222634915.png" alt="image-20220510222634915" style="zoom: 67%;" /></p></li><li><p><strong><span>应用层</span></strong><span>（Application Layer）: 当两个应用需要通信的时候，应用（进程中的线程）就调用传输层进行通信。从架构上说，应用层只专注于为用户提供价值即可，没有必要思考数据如何传输。</span></p></li><li><p><strong><span>传输层</span></strong><span>（Transport Layer）: 传输层为应用提供通信能力，怎么样按照协议比如TCP协议打包数据然后通过网络层在网络之间传输。比如浏览器想访问服务器，浏览器程序就会调用传输层程序；Web 服务接收浏览器的请求，Web 服务程序就会调用传输层程序接收数据。</span><strong><span>端口号</span></strong><span> :一台服务器上可能有很多个应用在接收数据，需要一个编号将应用区分开，这个编号就是端口号。 (TCP Transmission Control Protocol）是目前世界上应用最广泛的传输层协议.</span></p></li><li><p><strong><span>网络层</span></strong><span>： 负责将传输层数据的实际的传输功能。因为从一个主机到另一个主机传输数据的网络环境是非常复杂的。中间会通过各种各样的线路，有形形色色的交叉路口——有各式各样的路径和节点需要选择，所以要将传输路线的选择问题抽象处理，放在网络层处理。</span></p></li><li><p><strong><span>IP 协议</span></strong><span>（Internet Protocol）是目前起到统治地位的</span><strong><span>网络层协议</span></strong><span>。IP 协议会将传输层的封包再次切分，得到 IP 封包。网络层负责实际将数据从一台主机传输到另一台主机（Host To Host），因此网络层需要区分主机的编号。负责寻址（Addressing，下一个ip子地址怎么走）和路由（routing，根据下一个ip目的地选择路径）。</span></p></li><li><p><strong><span>数据链路层（Data Link Layer）</span></strong><span>在两个网络实体之间提供数据链路连接的创建、维持和释放管理。提供跨网络之间的传输的IP查找，比如家庭的局域网是一个网络，一个基站覆盖的周边也是另一个网络，数据链路层去识别网络中的设备，让数据在一个链路（网络中的路径）中传递。</span></p></li><li><p><strong><span>物理层（Physical Layer）</span></strong><span>当数据在实际的设备间传递时，可能会用电线、电缆、光纤、卫星、无线等各种通信手段。因此，还需要一层将光电信号、设备差异封装起来，为数据链路层提供二进制传输的服务。</span></p></li><li><p><strong><span>多路复用（Multiplex）</span></strong><span>：让多个信号（例如：请求/返回等）共用一个信道（例如：一个 TCP 连接）。它有两个明显的优势。</span></p><ul><li><span>提升吞吐量。多一个信号被紧密编排在一起（例如：TCP 多路复用节省了多次连接的数据），这样网络不容易空载。</span></li></ul><ul><li><span>多个信号间隔离。信号间并行传输，并且隔离，不会互相影响。</span></li></ul></li></ul><p><span> </span></p><h4 id='31-udp-协议udp-和-tcp-相比快在哪里'><span>31 UDP 协议：UDP 和 TCP 相比快在哪里？</span></h4><ul><li><strong><span>可靠性（Reliability）</span></strong><span>如果一个网络协议是可靠的，那么它能够保证数据被无损地传送到目的地。TCP 协议，是一个支持可靠性的协议。UDP 协议，是一个不支持可靠性的协议。</span></li><li><span>校验和（Checksum），非常常见的可靠性检查手段。TCP 和 UDP 都支持最基本的校验和算法。但二者的区别是，TCP 如果发现校验核对不上，也就是数据损坏，会主动丢失这个封包并且重发。而 UDP 什么都不会处理，UDP 把处理的权利交给使用它的程序员。</span></li><li><strong><span>请求/应答/连接模型</span></strong><span>：在 TCP 协议当中，任何一方向另一方发送信息，另一方都需要给予一个应答。如果发送方在一定的时间内没有获得应答，发送方就会认为自己的信息没有到达目的地，中途发生了损坏或者丢失等，因此发送方会选择重发这条消息。TCP 实现了请求、响应和连接的模型，UDP 没有实现这个模型。</span></li><li><strong><span>TCP 的三次握手</span></strong><span> ： 在 TCP 协议当中。我们假设 Alice 和 Bob 是两个通信进程。当 Alice 想要和 Bob 建立连接的时候，Alice 需要发送一个请求建立连接的消息给 Bob。这种请求建立连接的消息在 TCP 协议中称为</span><strong><span>同步</span></strong><span>（Synchronization， </span><strong><span>SYN</span></strong><span>）。而 Bob 收到 SYN，必须马上给 Alice 一个响应。这个响应在 TCP 协议当中称为</span><strong><span>响应</span></strong><span>（Acknowledgement，</span><strong><span>ACK</span></strong><span>）</span></li><li><span>当 Alice 给 Bob SYN，Bob 给 Alice ACK，这个时候，对 Alice 而言，连接就建立成功了。但是 TCP 是一个双工协议。所谓双工协议，代表数据可以双向传送。虽然对 Alice 而言，连接建立成功了。但是对 Bob 而言，连接还没有建立。因为如果这个时候，Bob 马上给 Alice 发送信息，信息可能先于 Bob 的 ACK 到达 Alice，但这个时候 Alice 还不知道连接建立成功。 所以解决的办法就是 Bob 再给 Alice 发一次 SYN ，Alice 再给 Bob 一个 ACK。以上就是 TCP 的三次握手内容。注意Bob 给 Alice 的 ACK ，可以和 Bob 向 Alice 发起的 SYN 合并，称为一条 SYN-ACK 消息，所以4次握手可以简化为3次。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220511210837857.png" referrerpolicy="no-referrer" alt="image-20220511210837857"></li></ul><ul><li><strong><span>TCP 的四次挥手</span></strong><span> 断开连接，中断连接的请求我们称为 Finish（用 FIN 表示），由于Bob的FIN 和ACK不能合并，原因是在挥手的过程中，Alice 和 Bob 都可能有未完成的工作。比如对 Bob 而言，可能还存在之前发给 Alice 但是还没有收到 ACK 的请求。因此，Bob 收到 Alice 的 FIN 后，就马上给 ACK。但是 Bob 会在自己准备妥当后，再发送 FIN 给 Alice。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220511211028524.png" referrerpolicy="no-referrer" alt="image-20220511211028524"></li></ul><p>&nbsp;</p><h4 id='32--linux-的-io-模式selectpollepoll-的区别'><span>32  Linux 的 I/O 模式：select/poll/epoll 的区别</span></h4><ul><li><span>怎么样通过操作系统的底层优化，提高系统的I/O能力，从而提高并发能力</span></li><li><span>从网卡到操作系统： 数据通过网卡结果操作系统处理需要将它们保存下来，因为网卡没有大缓存，最终需要到达内存，Linux 中用一个双向链表作为缓冲区，你可以观察下图中的 Buffer，看上去像一个有很多个凹槽的线性结构，每个凹槽（节点）可以存储一个封包，这个封包可以从网络层看（IP 封包），也可以从传输层看（TCP 封包）。操作系统不断地从 Buffer 中取出数据，数据通过一个协议栈，你可以把它理解成很多个协议的集合。协议栈中数据封包找到对应的协议程序处理完之后，就会形成 Socket 文件。</span></li><li><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220511212446463.png" referrerpolicy="no-referrer" alt="image-20220511212446463"></li></ul><ul><li><strong><span>Socket 编程模型</span></strong><span>，Socket 连接了应用和协议，如果应用层的程序想要传输数据，就创建一个 Socket。应用向 Socket 中写入数据，相当于将数据发送给了另一个应用。应用从 Socket 中读取数据，相当于接收另一个应用发送的数据。而具体的操作就是由 Socket 进行封装。</span><img src="C:\Users\carso\AppData\Roaming\Typora\typora-user-images\image-20220511212710074.png" referrerpolicy="no-referrer" alt="image-20220511212710074"></li></ul><ul><li></li></ul><p>&nbsp;</p><p>&nbsp;</p></div></div>
</body>
</html>